home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
DDJMAG
/
DDJ9203.ZIP
/
GRAPHPRG.ASC
< prev
next >
Wrap
Text File
|
1992-01-24
|
30KB
|
691 lines
_GRAPHICS PROGRAMMING COLUMN_
by Michael Abrash
[LISTING ONE]
/* 3D animation program to rotate 12 cubes. Uses fixed point. All C code
tested with Borland C++ 3.0 in C compilation mode and the small model. */
#include <conio.h>
#include <dos.h>
#include "polygon.h"
/* Base offset of page to which to draw */
unsigned int CurrentPageBase = 0;
/* Clip rectangle; clips to the screen */
int ClipMinX = 0, ClipMinY = 0;
int ClipMaxX = SCREEN_WIDTH, ClipMaxY = SCREEN_HEIGHT;
static unsigned int PageStartOffsets[2] =
{PAGE0_START_OFFSET,PAGE1_START_OFFSET};
int DisplayedPage, NonDisplayedPage;
int RecalcAllXforms = 1, NumObjects = 0;
Xform WorldViewXform; /* initialized from floats */
/* Pointers to objects */
Object *ObjectList[MAX_OBJECTS];
void main() {
int Done = 0, i;
Object *ObjectPtr;
union REGS regset;
InitializeFixedPoint(); /* set up fixed-point data */
InitializeCubes(); /* set up cubes and add them to object list; other
objects would be initialized now, if there were any */
Set320x240Mode(); /* set the screen to mode X */
ShowPage(PageStartOffsets[DisplayedPage = 0]);
/* Keep transforming the cube, drawing it to the undisplayed page,
and flipping the page to show it */
do {
/* For each object, regenerate viewing info, if necessary */
for (i=0; i<NumObjects; i++) {
if ((ObjectPtr = ObjectList[i])->RecalcXform ||
RecalcAllXforms) {
ObjectPtr->RecalcFunc(ObjectPtr);
ObjectPtr->RecalcXform = 0;
}
}
RecalcAllXforms = 0;
CurrentPageBase = /* select other page for drawing to */
PageStartOffsets[NonDisplayedPage = DisplayedPage ^ 1];
/* For each object, clear the portion of the non-displayed page
that was drawn to last time, then reset the erase extent */
for (i=0; i<NumObjects; i++) {
ObjectPtr = ObjectList[i];
FillRectangleX(ObjectPtr->EraseRect[NonDisplayedPage].Left,
ObjectPtr->EraseRect[NonDisplayedPage].Top,
ObjectPtr->EraseRect[NonDisplayedPage].Right,
ObjectPtr->EraseRect[NonDisplayedPage].Bottom,
CurrentPageBase, 0);
ObjectPtr->EraseRect[NonDisplayedPage].Left =
ObjectPtr->EraseRect[NonDisplayedPage].Top = 0x7FFF;
ObjectPtr->EraseRect[NonDisplayedPage].Right =
ObjectPtr->EraseRect[NonDisplayedPage].Bottom = 0;
}
/* Draw all objects */
for (i=0; i<NumObjects; i++)
ObjectList[i]->DrawFunc(ObjectList[i]);
/* Flip to display the page into which we just drew */
ShowPage(PageStartOffsets[DisplayedPage = NonDisplayedPage]);
/* Move and reorient each object */
for (i=0; i<NumObjects; i++)
ObjectList[i]->MoveFunc(ObjectList[i]);
if (kbhit())
if (getch() == 0x1B) Done = 1; /* Esc to exit */
} while (!Done);
/* Return to text mode and exit */
regset.x.ax = 0x0003; /* AL = 3 selects 80x25 text mode */
int86(0x10, ®set, ®set);
exit(1);
}
[LISTING TWO]
/* Transforms all vertices in the specified polygon-based object into view
space, then perspective projects them to screen space and maps them to screen
coordinates, storing results in the object. Recalculates object->view
transformation because only if transform changes would we bother
to retransform the vertices. */
#include <math.h>
#include "polygon.h"
void XformAndProjectPObject(PObject * ObjectToXform)
{
int i, NumPoints = ObjectToXform->NumVerts;
Point3 * Points = ObjectToXform->VertexList;
Point3 * XformedPoints = ObjectToXform->XformedVertexList;
Point3 * ProjectedPoints = ObjectToXform->ProjectedVertexList;
Point * ScreenPoints = ObjectToXform->ScreenVertexList;
/* Recalculate the object->view transform */
ConcatXforms(WorldViewXform, ObjectToXform->XformToWorld,
ObjectToXform->XformToView);
/* Apply that new transformation and project the points */
for (i=0; i<NumPoints; i++, Points++, XformedPoints++,
ProjectedPoints++, ScreenPoints++) {
/* Transform to view space */
XformVec(ObjectToXform->XformToView, (Fixedpoint *) Points,
(Fixedpoint *) XformedPoints);
/* Perspective-project to screen space */
ProjectedPoints->X =
FixedMul(FixedDiv(XformedPoints->X, XformedPoints->Z),
DOUBLE_TO_FIXED(PROJECTION_RATIO * (SCREEN_WIDTH/2)));
ProjectedPoints->Y =
FixedMul(FixedDiv(XformedPoints->Y, XformedPoints->Z),
DOUBLE_TO_FIXED(PROJECTION_RATIO * (SCREEN_WIDTH/2)));
ProjectedPoints->Z = XformedPoints->Z;
/* Convert to screen coordinates. The Y coord is negated to flip from
increasing Y being up to increasing Y being down, as expected by polygon
filler. Add in half the screen width and height to center on screen */
ScreenPoints->X = ((int) ((ProjectedPoints->X +
DOUBLE_TO_FIXED(0.5)) >> 16)) + SCREEN_WIDTH/2;
ScreenPoints->Y = (-((int) ((ProjectedPoints->Y +
DOUBLE_TO_FIXED(0.5)) >> 16))) + SCREEN_HEIGHT/2;
}
}
[LISTING THREE]
/* Routines to perform incremental rotations around the three axes. */
#include <math.h>
#include "polygon.h"
/* Concatenate a rotation by Angle around the X axis to transformation in
XformToChange, placing the result back into XformToChange. */
void AppendRotationX(Xform XformToChange, double Angle)
{
Fixedpoint Temp10, Temp11, Temp12, Temp20, Temp21, Temp22;
Fixedpoint CosTemp = DOUBLE_TO_FIXED(cos(Angle));
Fixedpoint SinTemp = DOUBLE_TO_FIXED(sin(Angle));
/* Calculate the new values of the six affected matrix entries */
Temp10 = FixedMul(CosTemp, XformToChange[1][0]) +
FixedMul(-SinTemp, XformToChange[2][0]);
Temp11 = FixedMul(CosTemp, XformToChange[1][1]) +
FixedMul(-SinTemp, XformToChange[2][1]);
Temp12 = FixedMul(CosTemp, XformToChange[1][2]) +
FixedMul(-SinTemp, XformToChange[2][2]);
Temp20 = FixedMul(SinTemp, XformToChange[1][0]) +
FixedMul(CosTemp, XformToChange[2][0]);
Temp21 = FixedMul(SinTemp, XformToChange[1][1]) +
FixedMul(CosTemp, XformToChange[2][1]);
Temp22 = FixedMul(SinTemp, XformToChange[1][2]) +
FixedMul(CosTemp, XformToChange[2][2]);
/* Put the results back into XformToChange */
XformToChange[1][0] = Temp10; XformToChange[1][1] = Temp11;
XformToChange[1][2] = Temp12; XformToChange[2][0] = Temp20;
XformToChange[2][1] = Temp21; XformToChange[2][2] = Temp22;
}
/* Concatenate a rotation by Angle around the Y axis to transformation in
XformToChange, placing the result back into XformToChange. */
void AppendRotationY(Xform XformToChange, double Angle)
{
Fixedpoint Temp00, Temp01, Temp02, Temp20, Temp21, Temp22;
Fixedpoint CosTemp = DOUBLE_TO_FIXED(cos(Angle));
Fixedpoint SinTemp = DOUBLE_TO_FIXED(sin(Angle));
/* Calculate the new values of the six affected matrix entries */
Temp00 = FixedMul(CosTemp, XformToChange[0][0]) +
FixedMul(SinTemp, XformToChange[2][0]);
Temp01 = FixedMul(CosTemp, XformToChange[0][1]) +
FixedMul(SinTemp, XformToChange[2][1]);
Temp02 = FixedMul(CosTemp, XformToChange[0][2]) +
FixedMul(SinTemp, XformToChange[2][2]);
Temp20 = FixedMul(-SinTemp, XformToChange[0][0]) +
FixedMul( CosTemp, XformToChange[2][0]);
Temp21 = FixedMul(-SinTemp, XformToChange[0][1]) +
FixedMul(CosTemp, XformToChange[2][1]);
Temp22 = FixedMul(-SinTemp, XformToChange[0][2]) +
FixedMul(CosTemp, XformToChange[2][2]);
/* Put the results back into XformToChange */
XformToChange[0][0] = Temp00; XformToChange[0][1] = Temp01;
XformToChange[0][2] = Temp02; XformToChange[2][0] = Temp20;
XformToChange[2][1] = Temp21; XformToChange[2][2] = Temp22;
}
/* Concatenate a rotation by Angle around the Z axis to transformation in
XformToChange, placing the result back into XformToChange. */
void AppendRotationZ(Xform XformToChange, double Angle)
{
Fixedpoint Temp00, Temp01, Temp02, Temp10, Temp11, Temp12;
Fixedpoint CosTemp = DOUBLE_TO_FIXED(cos(Angle));
Fixedpoint SinTemp = DOUBLE_TO_FIXED(sin(Angle));
/* Calculate the new values of the six affected matrix entries */
Temp00 = FixedMul(CosTemp, XformToChange[0][0]) +
FixedMul(-SinTemp, XformToChange[1][0]);
Temp01 = FixedMul(CosTemp, XformToChange[0][1]) +
FixedMul(-SinTemp, XformToChange[1][1]);
Temp02 = FixedMul(CosTemp, XformToChange[0][2]) +
FixedMul(-SinTemp, XformToChange[1][2]);
Temp10 = FixedMul(SinTemp, XformToChange[0][0]) +
FixedMul(CosTemp, XformToChange[1][0]);
Temp11 = FixedMul(SinTemp, XformToChange[0][1]) +
FixedMul(CosTemp, XformToChange[1][1]);
Temp12 = FixedMul(SinTemp, XformToChange[0][2]) +
FixedMul(CosTemp, XformToChange[1][2]);
/* Put the results back into XformToChange */
XformToChange[0][0] = Temp00; XformToChange[0][1] = Temp01;
XformToChange[0][2] = Temp02; XformToChange[1][0] = Temp10;
XformToChange[1][1] = Temp11; XformToChange[1][2] = Temp12;
}
[LISTING FOUR]
/* Fixed point matrix arithmetic functions */
#include "polygon.h"
/* Matrix multiplies Xform by SourceVec, and stores the result in DestVec.
Multiplies a 4x4 matrix times a 4x1 matrix; the result is a 4x1 matrix. Cheats
by assuming the W coord is 1 and bottom row of matrix is 0 0 0 1, and doesn't
bother to set the W coordinate of the destination */
void XformVec(Xform WorkingXform, Fixedpoint *SourceVec,
Fixedpoint *DestVec)
{
int i;
for (i=0; i<3; i++)
DestVec[i] = FixedMul(WorkingXform[i][0], SourceVec[0]) +
FixedMul(WorkingXform[i][1], SourceVec[1]) +
FixedMul(WorkingXform[i][2], SourceVec[2]) +
WorkingXform[i][3]; /* no need to multiply by W = 1 */
}
/* Matrix multiplies SourceXform1 by SourceXform2 and stores result in
DestXform. Multiplies a 4x4 matrix times a 4x4 matrix; result is a 4x4 matrix.
Cheats by assuming bottom row of each matrix is 0 0 0 1, and doesn't bother
to set the bottom row of the destination */
void ConcatXforms(Xform SourceXform1, Xform SourceXform2,
Xform DestXform)
{
int i, j;
for (i=0; i<3; i++) {
for (j=0; j<4; j++)
DestXform[i][j] =
FixedMul(SourceXform1[i][0], SourceXform2[0][j]) +
FixedMul(SourceXform1[i][1], SourceXform2[1][j]) +
FixedMul(SourceXform1[i][2], SourceXform2[2][j]) +
SourceXform1[i][3];
}
}
[LISTING FIVE]
/* Set up basic data that needs to be in fixed point, to avoid data
definition hassles. */
#include "polygon.h"
/* All vertices in the basic cube */
static IntPoint3 IntCubeVerts[NUM_CUBE_VERTS] = {
{15,15,15},{15,15,-15},{15,-15,15},{15,-15,-15},
{-15,15,15},{-15,15,-15},{-15,-15,15},{-15,-15,-15} };
/* Transformation from world space into view space (no transformation,
currently) */
static int IntWorldViewXform[3][4] = {
{1,0,0,0}, {0,1,0,0}, {0,0,1,0}};
void InitializeFixedPoint()
{
int i, j;
for (i=0; i<3; i++)
for (j=0; j<4; j++)
WorldViewXform[i][j] = INT_TO_FIXED(IntWorldViewXform[i][j]);
for (i=0; i<NUM_CUBE_VERTS; i++) {
CubeVerts[i].X = INT_TO_FIXED(IntCubeVerts[i].X);
CubeVerts[i].Y = INT_TO_FIXED(IntCubeVerts[i].Y);
CubeVerts[i].Z = INT_TO_FIXED(IntCubeVerts[i].Z);
}
}
[LISTING SIX]
/* Rotates and moves a polygon-based object around the three axes.
Movement is implemented only along the Z axis currently. */
#include "polygon.h"
void RotateAndMovePObject(PObject * ObjectToMove)
{
if (--ObjectToMove->RDelayCount == 0) { /* rotate */
ObjectToMove->RDelayCount = ObjectToMove->RDelayCountBase;
if (ObjectToMove->Rotate.RotateX != 0.0)
AppendRotationX(ObjectToMove->XformToWorld,
ObjectToMove->Rotate.RotateX);
if (ObjectToMove->Rotate.RotateY != 0.0)
AppendRotationY(ObjectToMove->XformToWorld,
ObjectToMove->Rotate.RotateY);
if (ObjectToMove->Rotate.RotateZ != 0.0)
AppendRotationZ(ObjectToMove->XformToWorld,
ObjectToMove->Rotate.RotateZ);
ObjectToMove->RecalcXform = 1;
}
/* Move in Z, checking for bouncing and stopping */
if (--ObjectToMove->MDelayCount == 0) {
ObjectToMove->MDelayCount = ObjectToMove->MDelayCountBase;
ObjectToMove->XformToWorld[2][3] += ObjectToMove->Move.MoveZ;
if (ObjectToMove->XformToWorld[2][3]>ObjectToMove->Move.MaxZ)
ObjectToMove->Move.MoveZ = 0; /* stop if close enough */
ObjectToMove->RecalcXform = 1;
}
}
[LISTING SEVEN]
/* Draws all visible faces in specified polygon-based object. Object must have
previously been transformed and projected, so that ScreenVertexList array is
filled in. */
#include "polygon.h"
void DrawPObject(PObject * ObjectToXform)
{
int i, j, NumFaces = ObjectToXform->NumFaces, NumVertices;
int * VertNumsPtr;
Face * FacePtr = ObjectToXform->FaceList;
Point * ScreenPoints = ObjectToXform->ScreenVertexList;
long v1, v2, w1, w2;
Point Vertices[MAX_POLY_LENGTH];
PointListHeader Polygon;
/* Draw each visible face (polygon) of the object in turn */
for (i=0; i<NumFaces; i++, FacePtr++) {
NumVertices = FacePtr->NumVerts;
/* Copy over the face's vertices from the vertex list */
for (j=0, VertNumsPtr=FacePtr->VertNums; j<NumVertices; j++)
Vertices[j] = ScreenPoints[*VertNumsPtr++];
/* Draw only if outside face showing (if the normal to the
polygon points toward viewer; that is, has a positive Z component) */
v1 = Vertices[1].X - Vertices[0].X;
w1 = Vertices[NumVertices-1].X - Vertices[0].X;
v2 = Vertices[1].Y - Vertices[0].Y;
w2 = Vertices[NumVertices-1].Y - Vertices[0].Y;
if ((v1*w2 - v2*w1) > 0) {
/* It is facing the screen, so draw */
/* Appropriately adjust the extent of the rectangle used to
erase this object later */
for (j=0; j<NumVertices; j++) {
if (Vertices[j].X >
ObjectToXform->EraseRect[NonDisplayedPage].Right)
if (Vertices[j].X < SCREEN_WIDTH)
ObjectToXform->EraseRect[NonDisplayedPage].Right =
Vertices[j].X;
else ObjectToXform->EraseRect[NonDisplayedPage].Right =
SCREEN_WIDTH;
if (Vertices[j].Y >
ObjectToXform->EraseRect[NonDisplayedPage].Bottom)
if (Vertices[j].Y < SCREEN_HEIGHT)
ObjectToXform->EraseRect[NonDisplayedPage].Bottom =
Vertices[j].Y;
else ObjectToXform->EraseRect[NonDisplayedPage].Bottom=
SCREEN_HEIGHT;
if (Vertices[j].X <
ObjectToXform->EraseRect[NonDisplayedPage].Left)
if (Vertices[j].X > 0)
ObjectToXform->EraseRect[NonDisplayedPage].Left =
Vertices[j].X;
else ObjectToXform->EraseRect[NonDisplayedPage].Left=0;
if (Vertices[j].Y <
ObjectToXform->EraseRect[NonDisplayedPage].Top)
if (Vertices[j].Y > 0)
ObjectToXform->EraseRect[NonDisplayedPage].Top =
Vertices[j].Y;
else ObjectToXform->EraseRect[NonDisplayedPage].Top=0;
}
/* Draw the polygon */
DRAW_POLYGON(Vertices, NumVertices, FacePtr->Color, 0, 0);
}
}
}
[LISTING EIGHT]
/* Initializes the cubes and adds them to the object list. */
#include <stdlib.h>
#include <math.h>
#include "polygon.h"
#define ROT_6 (M_PI / 30.0) /* rotate 6 degrees at a time */
#define ROT_3 (M_PI / 60.0) /* rotate 3 degrees at a time */
#define ROT_2 (M_PI / 90.0) /* rotate 2 degrees at a time */
#define NUM_CUBES 12 /* # of cubes */
Point3 CubeVerts[NUM_CUBE_VERTS]; /* set elsewhere, from floats */
/* Vertex indices for individual cube faces */
static int Face1[] = {1,3,2,0};
static int Face2[] = {5,7,3,1};
static int Face3[] = {4,5,1,0};
static int Face4[] = {3,7,6,2};
static int Face5[] = {5,4,6,7};
static int Face6[] = {0,2,6,4};
static int *VertNumList[]={Face1, Face2, Face3, Face4, Face5, Face6};
static int VertsInFace[]={ sizeof(Face1)/sizeof(int),
sizeof(Face2)/sizeof(int), sizeof(Face3)/sizeof(int),
sizeof(Face4)/sizeof(int), sizeof(Face5)/sizeof(int),
sizeof(Face6)/sizeof(int) };
/* X, Y, Z rotations for cubes */
static RotateControl InitialRotate[NUM_CUBES] = {
{0.0,ROT_6,ROT_6},{ROT_3,0.0,ROT_3},{ROT_3,ROT_3,0.0},
{ROT_3,-ROT_3,0.0},{-ROT_3,ROT_2,0.0},{-ROT_6,-ROT_3,0.0},
{ROT_3,0.0,-ROT_6},{-ROT_2,0.0,ROT_3},{-ROT_3,0.0,-ROT_3},
{0.0,ROT_2,-ROT_2},{0.0,-ROT_3,ROT_3},{0.0,-ROT_6,-ROT_6},};
static MoveControl InitialMove[NUM_CUBES] = {
{0,0,80,0,0,0,0,0,-350},{0,0,80,0,0,0,0,0,-350},
{0,0,80,0,0,0,0,0,-350},{0,0,80,0,0,0,0,0,-350},
{0,0,80,0,0,0,0,0,-350},{0,0,80,0,0,0,0,0,-350},
{0,0,80,0,0,0,0,0,-350},{0,0,80,0,0,0,0,0,-350},
{0,0,80,0,0,0,0,0,-350},{0,0,80,0,0,0,0,0,-350},
{0,0,80,0,0,0,0,0,-350},{0,0,80,0,0,0,0,0,-350}, };
/* Face colors for various cubes */
static int Colors[NUM_CUBES][NUM_CUBE_FACES] = {
{15,14,12,11,10,9},{1,2,3,4,5,6},{35,37,39,41,43,45},
{47,49,51,53,55,57},{59,61,63,65,67,69},{71,73,75,77,79,81},
{83,85,87,89,91,93},{95,97,99,101,103,105},
{107,109,111,113,115,117},{119,121,123,125,127,129},
{131,133,135,137,139,141},{143,145,147,149,151,153} };
/* Starting coordinates for cubes in world space */
static int CubeStartCoords[NUM_CUBES][3] = {
{100,0,-6000}, {100,70,-6000}, {100,-70,-6000}, {33,0,-6000},
{33,70,-6000}, {33,-70,-6000}, {-33,0,-6000}, {-33,70,-6000},
{-33,-70,-6000},{-100,0,-6000}, {-100,70,-6000}, {-100,-70,-6000}};
/* Delay counts (speed control) for cubes */
static int InitRDelayCounts[NUM_CUBES] = {1,2,1,2,1,1,1,1,1,2,1,1};
static int BaseRDelayCounts[NUM_CUBES] = {1,2,1,2,2,1,1,1,2,2,2,1};
static int InitMDelayCounts[NUM_CUBES] = {1,1,1,1,1,1,1,1,1,1,1,1};
static int BaseMDelayCounts[NUM_CUBES] = {1,1,1,1,1,1,1,1,1,1,1,1};
void InitializeCubes()
{
int i, j, k;
PObject *WorkingCube;
for (i=0; i<NUM_CUBES; i++) {
if ((WorkingCube = malloc(sizeof(PObject))) == NULL) {
printf("Couldn't get memory\n"); exit(1); }
WorkingCube->DrawFunc = DrawPObject;
WorkingCube->RecalcFunc = XformAndProjectPObject;
WorkingCube->MoveFunc = RotateAndMovePObject;
WorkingCube->RecalcXform = 1;
for (k=0; k<2; k++) {
WorkingCube->EraseRect[k].Left =
WorkingCube->EraseRect[k].Top = 0x7FFF;
WorkingCube->EraseRect[k].Right = 0;
WorkingCube->EraseRect[k].Bottom = 0;
}
WorkingCube->RDelayCount = InitRDelayCounts[i];
WorkingCube->RDelayCountBase = BaseRDelayCounts[i];
WorkingCube->MDelayCount = InitMDelayCounts[i];
WorkingCube->MDelayCountBase = BaseMDelayCounts[i];
/* Set the object->world xform to none */
for (j=0; j<3; j++)
for (k=0; k<4; k++)
WorkingCube->XformToWorld[j][k] = INT_TO_FIXED(0);
WorkingCube->XformToWorld[0][0] =
WorkingCube->XformToWorld[1][1] =
WorkingCube->XformToWorld[2][2] =
WorkingCube->XformToWorld[3][3] = INT_TO_FIXED(1);
/* Set the initial location */
for (j=0; j<3; j++) WorkingCube->XformToWorld[j][3] =
INT_TO_FIXED(CubeStartCoords[i][j]);
WorkingCube->NumVerts = NUM_CUBE_VERTS;
WorkingCube->VertexList = CubeVerts;
WorkingCube->NumFaces = NUM_CUBE_FACES;
WorkingCube->Rotate = InitialRotate[i];
WorkingCube->Move.MoveX = INT_TO_FIXED(InitialMove[i].MoveX);
WorkingCube->Move.MoveY = INT_TO_FIXED(InitialMove[i].MoveY);
WorkingCube->Move.MoveZ = INT_TO_FIXED(InitialMove[i].MoveZ);
WorkingCube->Move.MinX = INT_TO_FIXED(InitialMove[i].MinX);
WorkingCube->Move.MinY = INT_TO_FIXED(InitialMove[i].MinY);
WorkingCube->Move.MinZ = INT_TO_FIXED(InitialMove[i].MinZ);
WorkingCube->Move.MaxX = INT_TO_FIXED(InitialMove[i].MaxX);
WorkingCube->Move.MaxY = INT_TO_FIXED(InitialMove[i].MaxY);
WorkingCube->Move.MaxZ = INT_TO_FIXED(InitialMove[i].MaxZ);
if ((WorkingCube->XformedVertexList =
malloc(NUM_CUBE_VERTS*sizeof(Point3))) == NULL) {
printf("Couldn't get memory\n"); exit(1); }
if ((WorkingCube->ProjectedVertexList =
malloc(NUM_CUBE_VERTS*sizeof(Point3))) == NULL) {
printf("Couldn't get memory\n"); exit(1); }
if ((WorkingCube->ScreenVertexList =
malloc(NUM_CUBE_VERTS*sizeof(Point))) == NULL) {
printf("Couldn't get memory\n"); exit(1); }
if ((WorkingCube->FaceList =
malloc(NUM_CUBE_FACES*sizeof(Face))) == NULL) {
printf("Couldn't get memory\n"); exit(1); }
/* Initialize the faces */
for (j=0; j<NUM_CUBE_FACES; j++) {
WorkingCube->FaceList[j].VertNums = VertNumList[j];
WorkingCube->FaceList[j].NumVerts = VertsInFace[j];
WorkingCube->FaceList[j].Color = Colors[i][j];
}
ObjectList[NumObjects++] = (Object *)WorkingCube;
}
}
[LISTING NINE]
; 386-specific fixed point multiply and divide.
; C near-callable as: Fixedpoint FixedMul(Fixedpoint M1, Fixedpoint M2);
; Fixedpoint FixedDiv(Fixedpoint Dividend, Fixedpoint Divisor);
; Tested with TASM 3.0.
.model small
.386
.code
public _FixedMul,_FixedDiv
; Multiplies two fixed-point values together.
FMparms struc
dw 2 dup(?) ;return address & pushed BP
M1 dd ?
M2 dd ?
FMparms ends
align 2
_FixedMul proc near
push bp
mov bp,sp
mov eax,[bp+M1]
imul dword ptr [bp+M2] ;multiply
add eax,8000h ;round by adding 2^(-16)
adc edx,0 ;whole part of result is in DX
shr eax,16 ;put the fractional part in AX
pop bp
ret
_FixedMul endp
; Divides one fixed-point value by another.
FDparms struc
dw 2 dup(?) ;return address & pushed BP
Dividend dd ?
Divisor dd ?
FDparms ends
align 2
_FixedDiv proc near
push bp
mov bp,sp
sub cx,cx ;assume positive result
mov eax,[bp+Dividend]
and eax,eax ;positive dividend?
jns FDP1 ;yes
inc cx ;mark it's a negative dividend
neg eax ;make the dividend positive
FDP1: sub edx,edx ;make it a 64-bit dividend, then shift
; left 16 bits so that result will be in EAX
rol eax,16 ;put fractional part of dividend in
; high word of EAX
mov dx,ax ;put whole part of dividend in DX
sub ax,ax ;clear low word of EAX
mov ebx,dword ptr [bp+Divisor]
and ebx,ebx ;positive divisor?
jns FDP2 ;yes
dec cx ;mark it's a negative divisor
neg ebx ;make divisor positive
FDP2: div ebx ;divide
shr ebx,1 ;divisor/2, minus 1 if the divisor is
adc ebx,0 ; even
dec ebx
cmp ebx,edx ;set Carry if remainder is at least
adc eax,0 ; half as large as the divisor, then
; use that to round up if necessary
and cx,cx ;should the result be made negative?
jz FDP3 ;no
neg eax ;yes, negate it
FDP3: mov edx,eax ;return result in DX:AX; fractional
; part is already in AX
shr edx,16 ;whole part of result in DX
pop bp
ret
_FixedDiv endp
end
[LISTING TEN]
/* POLYGON.H: Header file for polygon-filling code, also includes
a number of useful items for 3D animation. */
#define MAX_OBJECTS 100 /* max simultaneous # objects supported */
#define MAX_POLY_LENGTH 4 /* four vertices is the max per poly */
#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240
#define PAGE0_START_OFFSET 0
#define PAGE1_START_OFFSET (((long)SCREEN_HEIGHT*SCREEN_WIDTH)/4)
#define NUM_CUBE_VERTS 8 /* # of vertices per cube */
#define NUM_CUBE_FACES 6 /* # of faces per cube */
/* Ratio: distance from viewpoint to projection plane / width of
projection plane. Defines the width of the field of view. Lower
absolute values = wider fields of view; higher values = narrower */
#define PROJECTION_RATIO -2.0 /* negative because visible Z
coordinates are negative */
/* Draws the polygon described by the point list PointList in color
Color with all vertices offset by (X,Y) */
#define DRAW_POLYGON(PointList,NumPoints,Color,X,Y) \
Polygon.Length = NumPoints; Polygon.PointPtr = PointList; \
FillConvexPolygon(&Polygon, Color, X, Y);
#define INT_TO_FIXED(x) (((long)(int)x) << 16)
#define DOUBLE_TO_FIXED(x) ((long) (x * 65536.0 + 0.5))
typedef long Fixedpoint;
typedef Fixedpoint Xform[3][4];
/* Describes a single 2D point */
typedef struct { int X; int Y; } Point;
/* Describes a single 3D point in homogeneous coordinates; the W
coordinate isn't present, though; assumed to be 1 and implied */
typedef struct { Fixedpoint X, Y, Z; } Point3;
typedef struct { int X; int Y; int Z; } IntPoint3;
/* Describes a series of points (used to store a list of vertices that
describe a polygon; each vertex is assumed to connect to the two
adjacent vertices; last vertex is assumed to connect to first) */
typedef struct { int Length; Point * PointPtr; } PointListHeader;
/* Describes the beginning and ending X coordinates of a single
horizontal line */
typedef struct { int XStart; int XEnd; } HLine;
/* Describes a Length-long series of horizontal lines, all assumed to
be on contiguous scan lines starting at YStart and proceeding
downward (used to describe a scan-converted polygon to the
low-level hardware-dependent drawing code) */
typedef struct { int Length; int YStart; HLine * HLinePtr;} HLineList;
typedef struct { int Left, Top, Right, Bottom; } Rect;
/* Structure describing one face of an object (one polygon) */
typedef struct { int * VertNums; int NumVerts; int Color; } Face;
typedef struct { double RotateX, RotateY, RotateZ; } RotateControl;
typedef struct { Fixedpoint MoveX, MoveY, MoveZ, MinX, MinY, MinZ,
MaxX, MaxY, MaxZ; } MoveControl;
/* Fields common to every object */
#define BASE_OBJECT \
void (*DrawFunc)(); /* draws object */ \
void (*RecalcFunc)(); /* prepares object for drawing */ \
void (*MoveFunc)(); /* moves object */ \
int RecalcXform; /* 1 to indicate need to recalc */ \
Rect EraseRect[2]; /* rectangle to erase in each page */
/* Basic object */
typedef struct { BASE_OBJECT } Object;
/* Structure describing a polygon-based object */
typedef struct {
BASE_OBJECT
int RDelayCount, RDelayCountBase; /* controls rotation speed */
int MDelayCount, MDelayCountBase; /* controls movement speed */
Xform XformToWorld; /* transform from object->world space */
Xform XformToView; /* transform from object->view space */
RotateControl Rotate; /* controls rotation change over time */
MoveControl Move; /* controls object movement over time */
int NumVerts; /* # vertices in VertexList */
Point3 * VertexList; /* untransformed vertices */
Point3 * XformedVertexList; /* transformed into view space */
Point3 * ProjectedVertexList; /* projected into screen space */
Point * ScreenVertexList; /* converted to screen coordinates */
int NumFaces; /* # of faces in object */
Face * FaceList; /* pointer to face info */
} PObject;
extern void XformVec(Xform, Fixedpoint *, Fixedpoint *);
extern void ConcatXforms(Xform, Xform, Xform);
extern int FillConvexPolygon(PointListHeader *, int, int, int);
extern void Set320x240Mode(void);
extern void ShowPage(unsigned int);
extern void FillRectangleX(int, int, int, int, unsigned int, int);
extern void XformAndProjectPObject(PObject *);
extern void DrawPObject(PObject *);
extern void AppendRotationX(Xform, double);
extern void AppendRotationY(Xform, double);
extern void AppendRotationZ(Xform, double);
extern near Fixedpoint FixedMul(Fixedpoint, Fixedpoint);
extern near Fixedpoint FixedDiv(Fixedpoint, Fixedpoint);
extern void InitializeFixedPoint(void);
extern void RotateAndMovePObject(PObject *);
extern void InitializeCubes(void);
extern int DisplayedPage, NonDisplayedPage, RecalcAllXforms;
extern int NumObjects;
extern Xform WorldViewXform;
extern Object *ObjectList[];
extern Point3 CubeVerts[];